use ffmpeg::{
    av_dump_format_safe, av_fifo_write_safe, av_packet_alloc_safe, av_packet_move_ref_safe,
    av_packet_unref_safe, av_read_frame_safe,
    avformat_alloc_context_safe, avformat_close_input_safe, avformat_find_stream_info_safe,
    avformat_open_input_safe, avformat_seek_file_safe,
};
use ffmpeg_sys_next::{AVCodecParameters, AVFormatContext, AVMediaType, AVStream, AV_TIME_BASE, AVSEEK_FLAG_BACKWARD};
use std::{
    ffi::{c_void, CString}, ptr::null_mut, sync::{Arc, Condvar, Mutex}, thread::Builder, time::Duration
};
use crossbeam_channel::{select, Receiver};

use crate::{
    audio_player::AudioPlayer, video_player::VideoPlayer, Broadcaster, CustomError, MediaStatus, Packet, PacketFifo, Timer, MAX_QUEUE_SIZE, FormatContextSafe
};

pub struct MediaPlayer {
    pub media_path: CString,
    pub av_format_context: *mut AVFormatContext,
    pub video_index: isize,
    pub audio_index: isize,
    pub title: String,
    pub condvar: Arc<Condvar>,
    pub video_packet_fifo: Arc<Mutex<PacketFifo>>,
    pub audio_packet_fifo: Arc<Mutex<PacketFifo>>,
    pub timer: Arc<Mutex<Timer>>,
    pub media_status: Arc<MediaStatus>,
    pub seek_receiver: Option<Receiver<i8>>,
}

impl MediaPlayer {
    pub fn new(file_path: &str) -> Self {
        let title = get_title(file_path).unwrap();
        let media_path = CString::new(file_path.to_owned()).unwrap();
        MediaPlayer {
            media_path,
            av_format_context: null_mut(),
            video_index: -1,
            audio_index: -1,
            title,
            condvar: Arc::new(Condvar::new()),
            video_packet_fifo: Arc::new(Mutex::new(PacketFifo::new())),
            audio_packet_fifo: Arc::new(Mutex::new(PacketFifo::new())),
            timer: Arc::new(Mutex::new(Timer::new())),
            media_status: Arc::new(MediaStatus::new()),
            seek_receiver: None,
        }
    }

    pub fn avformat_alloc_context(mut self) -> Result<Self, CustomError> {
        let av_format_context = avformat_alloc_context_safe();
        if av_format_context.is_null() {
            println!("ERROR could not allocate memory for Format Context");
            return Err(CustomError::FfmepgError(
                "ERROR could not allocate memory for Format Context".to_owned(),
            ));
        }
        self.av_format_context = av_format_context;
        Ok(self)
    }

    pub fn avformat_open_input(mut self) -> Result<Self, CustomError> {
        if avformat_open_input_safe(
            &mut self.av_format_context,
            self.media_path.as_ptr(),
            null_mut(),
            null_mut(),
        ) != 0
        {
            println!("Couldn't open file. path: {:?}", self.media_path);
            return Err(CustomError::FfmepgError("Couldn't open file".to_owned()));
        }
        Ok(self)
    }

    pub fn avformat_find_stream_info(self) -> Result<Self, CustomError> {
        if avformat_find_stream_info_safe(self.av_format_context, null_mut()) != 0 {
            println!("Couldn't find stream information");
            return Err(CustomError::FfmepgError(
                "Couldn't find stream information".to_owned(),
            ));
        }
        // self.duration = unsafe { (*self.av_format_context).duration / AV_TIME_BASE as i64 };
        Ok(self)
    }

    pub fn av_dump_format(self) -> Result<Self, CustomError> {
        av_dump_format_safe(self.av_format_context, 0, self.media_path.as_ptr(), 0);
        Ok(self)
    }

    pub fn nb_streams(
        mut self,
    ) -> Result<(Self, Option<VideoPlayer>, Option<AudioPlayer>), CustomError> {
        let nb_streams = unsafe { (*self.av_format_context).nb_streams as isize };
        let sdl = sdl2::init().unwrap();
        let mut video_player_opt = None;
        let mut audio_player_opt = None;

        let broadcaster = Broadcaster::<i8>::new();
        let seek_receiver = broadcaster.subscribe();
        self.seek_receiver = Some(seek_receiver);
        
        ////遍历文件中包含的所有流媒体类型(视频流、音频流、字幕流等)
        for index in 0..nb_streams {
            let av_stream: *mut *mut AVStream = unsafe { (*self.av_format_context).streams.offset(index) };
            let codec_parameters: *mut AVCodecParameters = unsafe { (**av_stream).codecpar };
            
            
            if unsafe { (*codec_parameters).codec_type } == AVMediaType::AVMEDIA_TYPE_VIDEO {
                self.video_index = index;
                
                let video_seek_receiver = broadcaster.subscribe();
                let mut video_player = VideoPlayer::new(
                    self.title.clone(),
                    sdl.clone(),
                    self.video_packet_fifo.clone(),
                    self.condvar.clone(),
                    self.timer.clone(),
                    self.media_status.clone(),
                    broadcaster.clone(),
                    video_seek_receiver,
                );
                video_player.index = index;
                let video_width = unsafe { (*codec_parameters).width } as u32;
                let video_height = unsafe { (*codec_parameters).height } as u32;
                video_player.original_width = video_width;
                video_player.original_height = video_height;
                video_player.video_codec_parameters = codec_parameters;
                video_player.time_base = unsafe { (**av_stream).time_base };
                
                
                video_player_opt = Some(video_player);
            } else if unsafe { (*codec_parameters).codec_type } == AVMediaType::AVMEDIA_TYPE_AUDIO
            && -1 == self.audio_index
            {
                self.audio_index = index;
                
                let audio_seek_receiver = broadcaster.subscribe();
                let mut audio_player = AudioPlayer::new(
                    sdl.clone(), 
                    self.audio_packet_fifo.clone(), 
                    self.condvar.clone(), 
                    self.timer.clone(),
                    self.media_status.clone(),
                    audio_seek_receiver,
                );
                audio_player.index = index;
                audio_player.audio_codec_parameters = codec_parameters;
                audio_player.time_base = unsafe { (**av_stream).time_base };
                audio_player.channels = unsafe { (*codec_parameters).ch_layout.nb_channels };
                audio_player.sample_rate = unsafe { (*codec_parameters).sample_rate };
                audio_player.out_sample_rate = unsafe { (*codec_parameters).sample_rate };
                audio_player.out_channel_layout = unsafe { &(*codec_parameters).ch_layout };

                audio_player_opt = Some(audio_player);
            }
        }
        if self.video_index == -1 && self.audio_index == -1 {
            return Err(CustomError::FfmepgError("读取的文件无音视频流".to_owned()));
        }

        Ok((self, video_player_opt, audio_player_opt))
    }

    pub fn read_packet(self) -> Result<Self, CustomError> {
        let format_context = FormatContextSafe {
            av_format_context: self.av_format_context,
        };
        let video_index = self.video_index as i32;
        let audio_index = self.audio_index as i32;
        let video_fifo = self.video_packet_fifo.clone();
        let audio_fifo = self.audio_packet_fifo.clone();
        let condvar = self.condvar.clone();
        let media_status = self.media_status.clone();

        Builder::new()
            .name("read media thread".to_string())
            .spawn(move || {
                MediaPlayer::loop_read_packet(
                    format_context,
                    video_index,
                    audio_index,
                    video_fifo,
                    audio_fifo,
                    condvar,
                    media_status,
                ).unwrap();
            })
            .unwrap();
        Ok(self)
    }

    pub fn loop_read_packet(
        format_context: FormatContextSafe,
        video_index: i32,
        audio_index: i32,
        video_fifo: Arc<Mutex<PacketFifo>>,
        audio_fifo: Arc<Mutex<PacketFifo>>,
        condvar: Arc<Condvar>,
        media_status: Arc<MediaStatus>,
    ) -> Result<(), CustomError> {
        loop {
            media_status.check_pause()?;

            let dst_packet = av_packet_alloc_safe();
            {
                let packet = av_packet_alloc_safe();
                if av_read_frame_safe(format_context.av_format_context, packet) >= 0 {
                    if unsafe { (*packet).stream_index } == video_index as i32
                        || unsafe { (*packet).stream_index } == audio_index as i32
                    {
                        av_packet_move_ref_safe(dst_packet, packet);
                    } else {
                        av_packet_unref_safe(packet);
                        //字幕等其他信息暂时不做处理
                        continue;
                    }
                } else {
                    av_packet_unref_safe(packet);
                    {
                        let mut status_lock = media_status.status.lock().unwrap();
                        println!("set packet end");
                        (*status_lock).set_packet_end();
                    }

                    return Ok(());
                }
                av_packet_unref_safe(packet);
            }

            let stream_index = unsafe { (*dst_packet).stream_index };
            if stream_index == video_index as i32 {
                let mut packet_fifo = video_fifo.lock().unwrap();
                let size = packet_fifo.size();
                let curr_size = size + 1;
                packet_fifo.put_size(curr_size);
                if size > MAX_QUEUE_SIZE {
                    packet_fifo = condvar.wait(packet_fifo).unwrap();
                }
                let mut packet = Packet { pkt: dst_packet };
                av_fifo_write_safe(packet_fifo.fifo, &mut packet as *mut Packet as *const c_void, 1);
            } else if stream_index == audio_index as i32 {
                let mut packet_fifo = audio_fifo.lock().unwrap();
                let size = packet_fifo.size();
                let curr_size = size + 1;
                packet_fifo.put_size(curr_size);
                if size > MAX_QUEUE_SIZE {
                    packet_fifo = condvar.wait(packet_fifo).unwrap();
                }
                let mut packet = Packet { pkt: dst_packet };
                av_fifo_write_safe(packet_fifo.fifo, &mut packet as *mut Packet as *const c_void, 1);
            }
        }
    }

    pub fn seek(self) -> Result<Self, CustomError> {
        let seek_receiver = self.seek_receiver.clone().unwrap();
        loop {
            select! {
                recv(seek_receiver) -> seek => {
                    match seek {
                        Ok(incr) => {
                            {
                                let mut status_lock = self.media_status.status.lock().unwrap();
                                let seek_rel = {
                                    self.timer.lock().unwrap().total_elapsed().as_secs() as i64
                                };
                                let seek_pos = (seek_rel + incr as i64) * AV_TIME_BASE as i64;
                                let min_ts = i64::MIN;
                                let max_ts = i64::MAX;
                                let res = avformat_seek_file_safe(
                                    self.av_format_context, 
                                    -1, 
                                    min_ts,
                                    seek_pos,
                                    max_ts,
                                    AVSEEK_FLAG_BACKWARD
                                );
                                //println!("seek_pos: {}, min:{}, max:{}", seek_pos, min_ts, max_ts);
                                println!("seek: {}, res: {}", incr, res);

                                {
                                    if incr > 0 {
                                        let add = Duration::from_millis(incr as u64 * 1000);
                                        self.timer.lock().unwrap().add_time(add);
                                    } else {
                                        let subtract = Duration::from_millis(-incr as u64 * 1000);
                                        self.timer.lock().unwrap().subtract_time(subtract);
                                    }
                                }
                                std::thread::sleep(std::time::Duration::from_millis(100));
                                (*status_lock).set_resume();
                                self.media_status.condvar.notify_all();
                                self.condvar.notify_all();
                            }
                        },
                        _ => {},
                    };
                },
                default => {
                    let mut status_lock = self.media_status.status.lock().unwrap();
                    if (*status_lock).video_frame_end() && (*status_lock).audio_frame_end() {
                        (*status_lock).set_play_end();
                        break;
                    }
                },
            }
            std::thread::sleep(std::time::Duration::from_millis(100));
        }
        Ok(self)
    }
    
    pub fn close(&mut self) -> Result<&mut Self, CustomError> {
        avformat_close_input_safe(&mut self.av_format_context);
        Ok(self)
    }
}

pub fn get_title(path: &str) -> Option<String> {
    if let Some(sep) = path.rfind('/') {
        let file_name = &path[sep + 1..];
        if let Some(dot) = file_name.rfind('.') {
            Some(file_name[..dot].to_owned())
        } else {
            Some(file_name.to_owned())
        }
    } else {
        Some("Sdl player".to_owned())
    }
}
